/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University. All rights reserved.
 * 
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license; 2) All redistributions in binary form
 * must reproduce the above copyright notice, this list of conditions and the
 * disclaimer listed in this license in the documentation and/or other materials
 * provided with the distribution; 3) Any documentation included with all
 * redistributions must include the following acknowledgement:
 * 
 * "This product includes software developed by the Community Grids Lab. For
 * further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/."
 * 
 * Alternatively, this acknowledgement may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 * 
 * 4) The name Indiana University or Community Grids Lab or NaradaBrokering,
 * shall not be used to endorse or promote products derived from this software
 * without prior written permission from Indiana University. For written
 * permission, please contact the Advanced Research and Technology Institute
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5) Products
 * derived from this software may not be called NaradaBrokering, nor may Indiana
 * University or Community Grids Lab or NaradaBrokering appear in their name,
 * without prior written permission of ARTI.
 * 
 * 
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of any
 * other entity. Indiana University disclaims any liability to any recipient for
 * claims brought by any other entity based on infringement of intellectual
 * property rights or otherwise.
 * 
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.event.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import cgl.narada.event.EventHeaders;
import cgl.narada.event.EventID;

/**
 * This class encapsulates various headers which indicate, among other things,
 * if the event is (a) Persistent or transient (b) Has Correlation identifers
 * (c) Security and Integrity related information (d) Fragmentation and Slicing
 * related information (e) Compressed
 * 
 * @author Shrideep Pallickara $Date$ $Revision$
 */

public class EventHeadersImpl implements EventHeaders {

   private boolean hasTemplateId = false;

   private boolean hasTimestamp = false;

   private boolean hasSource = false;

   private boolean supressDistributionToSource = false;

   private boolean hasEventId = false;

   private boolean isCorrelated = false;

   private boolean hasTimeToLive = false;

   private boolean isSecure = false;

   private boolean hasIntegrity = false;

   private boolean isFragmented = false;

   private boolean isLastFragment = false;

   private boolean hasTotalNumOfFragmentsInfo = false;

   private boolean isSliced = false;

   private boolean hasPriorityInfo = false;

   private boolean hasApplicationInfo = false;

   private boolean isPersistent = false;

   private boolean isTransient = true;

   private boolean isCompressed = false;

   private boolean isPayloadCompressionFinalStep = false;

   private int snapshot;

   private int numOfHeaders;

   private String moduleName = "EventHeadersImpl: ";

   private int templateId = 0;

   private long timestamp = 0;

   private int source = 0;

   private EventID eventId = null;

   private EventID correlationId = null;

   private int timeToLive = 0;

   // HG: Security Related
   // HG: Additions for security related stuff...
   private boolean hasSignedSecurityToken = false;

   private byte[] signedSecurityTokenBytes = null;

   private boolean hasSecurityTimeStampedSequenceNo = false;

   private byte[] securityTimeStampedSeqNo = null;

   // Note: This digital signature is on the payload ONLY
   // Digital signature of the security token can be verified... since its
   // already signed !
   // ---
   private boolean hasSignature = false;

   private byte[] signature = null;

   private String signatureAlgorithm = null;

   private boolean hasCipherIV = false;

   private byte[] cipherIV = null;

   private boolean hasCipherModeAndPadding = false;

   private String cipherMode = null;

   private String cipherPaddingScheme = null;

   private boolean hasAssertions = false;

   private String assertions = "";

   private String digestScheme = "";

   private byte[] messageDigest = null;

   private String fragmentationId = null;

   private int fragmentNumber = 0;

   private int totalNumberOfFragments = 0;

   private long fragmentationEpoch = 0;

   private int sliceStartingPoint = 0;

   private int sliceEndingPoint = 0;

   private int priority = 0;

   private String compressionAlgo = "";

   private long originalPayloadLength = 0;

   private String applicationType = "";

   private boolean headerModified = false;

   private byte[] marshalledBytes = null;
   
   public EventHeadersImpl() {

   }

   public EventHeadersImpl(byte[] headerBytes) {
      ByteArrayInputStream baInputStream = new ByteArrayInputStream(headerBytes);
      DataInputStream din = new DataInputStream(baInputStream);
      // System.out.println(moduleName + "trying to read ..");
      try {
         /** Marshall the header snapshot first */
         snapshot = din.readInt();
         // System.out.println(ByteUtilities.printInt(snapshot));

         if (snapshot == 0) {
            System.out.println(moduleName
                               + "Event headers will revert to defaults");
            return;
         }

         /** Initialize the header boolean variable so we know what to expect */
         getHeadersFromSnapshot();

         if (hasTemplateId) {
            templateId = din.readInt();
         }

         if (hasTimestamp) {
            timestamp = din.readLong();
         }

         if (hasSource) {
            source = din.readInt();
         }

         if (hasEventId) {
            int _idLength = din.readInt();
            byte[] _eventIdBytes = new byte[_idLength];
            din.readFully(_eventIdBytes);
            eventId = new EventID(_eventIdBytes);
         }

         if (isCorrelated) {
            int _corrLength = din.readInt();
            byte[] _corrIdBytes = new byte[_corrLength];
            din.readFully(_corrIdBytes);
            correlationId = new EventID(_corrIdBytes);
         }

         if (hasTimeToLive) {
            timeToLive = din.readInt();
         }

         if (isSecure) {
            hasSignature = din.readBoolean();
            if (hasSignature) {
               byte[] _signatureAlgorithmBytes = new byte[din.readInt()];
               din.readFully(_signatureAlgorithmBytes);
               signatureAlgorithm = new String(_signatureAlgorithmBytes);
               signature = new byte[din.readInt()];
               din.readFully(signature);
            }

            hasCipherIV = din.readBoolean();
            if (hasCipherIV) {
               cipherIV = new byte[din.readInt()];
               din.readFully(cipherIV);
            }

            hasCipherModeAndPadding = din.readBoolean();
            if (hasCipherModeAndPadding) {
               byte[] _cipherModeBytes = new byte[din.readInt()];
               din.readFully(_cipherModeBytes);
               cipherMode = new String(_cipherModeBytes);

               byte[] _ciperPaddingSchemeBytes = new byte[din.readInt()];
               din.readFully(_ciperPaddingSchemeBytes);
               cipherPaddingScheme = new String(_ciperPaddingSchemeBytes);
            }

            hasAssertions = din.readBoolean();
            if (hasAssertions) {
               int _assertionLength = din.readInt();
               byte[] _assertionBytes = new byte[_assertionLength];
               din.readFully(_assertionBytes);
               assertions = new String(_assertionBytes);
            }

            // HG: UnMarshalling for Signed security token
            hasSignedSecurityToken = din.readBoolean();
            if (hasSignedSecurityToken) {
               int _sstLen = din.readInt();
               signedSecurityTokenBytes = new byte[_sstLen];
               din.readFully(signedSecurityTokenBytes);
            }

            hasSecurityTimeStampedSequenceNo = din.readBoolean();
            if (hasSecurityTimeStampedSequenceNo) {
               int len = din.readInt();
               securityTimeStampedSeqNo = new byte[len];
               din.readFully(securityTimeStampedSeqNo);
            }

            // ---

         }/* isSecure */

         if (hasIntegrity) {
            int _digestSchemeLength = din.readInt();
            byte[] _digestSchemeBytes = new byte[_digestSchemeLength];
            din.readFully(_digestSchemeBytes);
            digestScheme = new String(_digestSchemeBytes);
            int _digestLength = din.readInt();
            messageDigest = new byte[_digestLength];
            din.readFully(messageDigest);
         }

         if (isFragmented) {
            int _fragLength = din.readInt();
            byte[] _fragIdBytes = new byte[_fragLength];
            din.readFully(_fragIdBytes);
            fragmentationId = new String(_fragIdBytes);
            originalPayloadLength = din.readLong();
            fragmentNumber = din.readInt();
            fragmentationEpoch = din.readLong();
         }

         if (hasTotalNumOfFragmentsInfo) {
            totalNumberOfFragments = din.readInt();
         }

         if (hasPriorityInfo) {
            priority = din.readInt();
         }

         if (hasApplicationInfo) {
            int _appTypeLength = din.readInt();
            byte[] _appTypeBytes = new byte[_appTypeLength];
            din.readFully(_appTypeBytes);
            applicationType = new String(_appTypeBytes);
         }

         if (isCompressed) {
            int _compAlgoLength = din.readInt();
            byte[] _compAlgoBytes = new byte[_compAlgoLength];
            din.readFully(_compAlgoBytes);
            compressionAlgo = new String(_compAlgoBytes);
            originalPayloadLength = din.readLong();
         }

      } catch (IOException ioe) {
         System.out
                   .println(moduleName + "Error while marshalling EventHeaders");
      }
      /** end try-catch block */
   }

   /** ********************************************************************** */
   /** Implementation of methods in the Event Headers interface */
   /** ********************************************************************** */

   /** Indicates the templateId which this event conforms to */
   public int getTemplateId() {
      return templateId;
   }

   /**
    * Returns the timestamp for this event. This timestamp corresponds to the
    * value assigned by the time service
    */
   public long getTimeStamp() {
      return timestamp;
   }

   /** Returns the generator of this message */
   public int getSource() {
      return source;
   }

   /**
    * Indicates that the message should not be rerouted to it source, that is
    * contained in the event headers
    */
   public boolean supressDistributionToSource() {
      return supressDistributionToSource;
   }

   /** Indicates if there is an event id associated with this message */
   public boolean hasEventId() {
      return hasEventId;
   }

   /** Get the event identifier */
   public EventID getEventId() {
      return eventId;
   }

   /**
    * Indicates if this event is correlated with any other event. Casual
    * constraints may entail that the correlated event be delivered prior to the
    * delivery of this event.
    */
   public boolean isCorrelated() {
      return isCorrelated();
   }

   /** Returns the correlation identifier associated with the event */
   public EventID getCorrelationIdentifier() {
      return correlationId;
   }

   /** Returns the time to live */
   public int getTimeToLive() {
      return timeToLive;
   }

   /** Indicates if this event is a secure event */
   public boolean isSecure() {
      return isSecure;
   }

   /** Indicates if the event has a signature associated with it */
   public boolean hasSignedSecurityToken() {
      return hasSignedSecurityToken;
   }

   /** Indicates if the event has a signature associated with it */
   public boolean hasSignature() {
      return hasSignature;
   }

   /**
    * Retrieves the signature Algorithm. Available <i>only<i> if this is a
    * secure event.
    */
   public String getSignatureAlgorithm() {
      return signatureAlgorithm;
   }

   /**
    * Retrieve the signature associated with event. Available <i>only<i> if
    * this is a secure event
    */
   public byte[] getSignature() {
      return signature;
   }

   /** Indicates if there is an IV associated with the encrypted payload */
   public boolean hasCipherIV() {
      return hasCipherIV;
   }

   /**
    * Retrieves the IV associated with the encrypted payload. Available <i>only<i>
    * if this is a secure event
    */
   public byte[] getCipherIV() {
      return cipherIV;
   }

   /**
    * Retrieves the Cipher mode. Available <i>only<i> if this is a secure event
    */
   public String getCipherMode() {
      return cipherMode;
   }

   /**
    * Retrieves the Cipher padding scheme. Available <i>only<i> if this is a
    * secure event
    */
   public String getCipherPaddingScheme() {
      return cipherPaddingScheme;
   }

   /** Indicates if there are security assertions encapsulated in this event. */
   public boolean hasAssertions() {
      return hasAssertions;
   }

   /**
    * Retrieve SAML Assertions. Available <i>only<i> if this is a secure event
    */
   public String getAssertions() {
      return assertions;
   }

   /** Indicates if the event has integerity check information. */
   public boolean hasIntegrity() {
      return hasIntegrity;
   }

   /**
    * Returns the message digest scheme used for computing the digest. Available
    * <i>only<i> if this is a secure event or has integrity
    */
   public String getMessageDigestScheme() {
      return digestScheme;
   }

   /**
    * Gets the message digest for the payload. Available <i>only<i> if this is
    * a secure event or has integrity
    */
   public byte[] getMessageDigest() {
      return messageDigest;
   }

   /** Indicates if this event is a fragment of a larger event */
   public boolean isFragmented() {
      return isFragmented;
   }

   /**
    * Returns the fragmentation identifier associated with this fragment.
    * Fragments that are part of the same event have the same fragmentation
    * identifier, though each of them might have different event identifiers
    */
   public String getFragmentationIdentifier() {
      return fragmentationId;
   }

   /**
    * Indicates if this is the last fragment in the series of fragmented events
    */
   public boolean isLastFragment() {
      return isLastFragment;
   }

   /** Gets the fragment number associated with this fragment */
   public int getFragmentNumber() {
      return fragmentNumber;
   }

   /**
    * Indicates if there is information pertaining to the total number of
    * fragments
    */
   public boolean hasTotalNumberOfFragmentsInfo() {
      return hasTotalNumOfFragmentsInfo;
   }

   /** Gets the fragment number associated with this fragment */
   public int getTotalNumberOfFragments() {
      return totalNumberOfFragments;
   }

   /** Gets the epoch associated with the fragmentation */
   public long getFragmentationEpoch() {
      return fragmentationEpoch;
   }

   /*
    * Indicates if this is an event slice which contains a part of the orginal
    * transmitted payload. These are specifically used in cases where the device
    * might not prefer the retransmission of the entire event
    */
   public boolean isSliced() {
      return isSliced;
   }

   /**
    * Gets the slice from which point on the payload is included. For example
    * one may be interested in the payload from size 90026 to 190000
    */
   public int getSliceStartingPoint() {
      return sliceStartingPoint;
   }

   /** Gets the slice ending point */
   public int getSliceEndingPoint() {
      return sliceEndingPoint;
   }

   /** Indicates if this event includes priority information */
   public boolean hasPriorityInfo() {
      return hasPriorityInfo;
   }

   /** Gets the priority associated with this event */
   public int getPriority() {
      return priority;
   }

   /** Gets the application type associated with this event */
   public String getApplicationType() {
      return applicationType;
   }

   /**
    * Indicates if the event is a persistent one. If it is, the event needs to
    * be archived on storage. It is of course assumed that the event corresponds
    * to a template that supports reliable delivery
    */
   public boolean isPersistent() {
      return isPersistent;
   }

   /**
    * Indicates if this event is a transient event. If this is a transient event
    * it need not be archived on any storage
    */
   public boolean isTransient() {
      return isTransient;
   }

   /**
    * Indicates if the payload has been compressed. Such events are specifically
    * used in cases where the device's network utilization costs might be at a
    * premium.
    */
   public boolean isCompressed() {
      return isCompressed;
   }

   /** Get information regarding the compresssion algorithm being used */
   public String getCompressionAlgorithm() {
      return compressionAlgo;
   }

   /**
    * Gets the original payload length prior to compression/fragmentation. Will
    * return ZERO if the event doesn't have a compressed/fragmented payload
    */
   public long getOriginalPayloadLength() {
      return originalPayloadLength;
   }

   /**
    * Indicates if the payload was compressed after operations such as securing,
    * computing message digests were performed
    */
   public boolean isPayloadCompressionFinalStep() {
      return isPayloadCompressionFinalStep;
   }

   /** ********************************************************************** */
   /** Implementation of methods in the Event Headers interface complete */
   /** ********************************************************************** */

   protected void setTemplateInfo(int templateId) {
      if (templateId == 0) {
         return;
      }
      hasTemplateId = true;
      this.templateId = templateId;
      setHeaderModified();
   }

   protected void setSourceInformation(int source,
                                       boolean supressDistributionToSource) {
      if (source == 0) {
         return;
      }
      hasSource = true;
      this.source = source;
      this.supressDistributionToSource = supressDistributionToSource;
      setHeaderModified();
   }

   protected void setTimestamp(long timestamp) {
      if (timestamp == 0) {
         return;
      }
      this.timestamp = timestamp;
      hasTimestamp = true;
      setHeaderModified();
   }

   protected void setEventId(EventID eventId) {
      if (eventId == null) {
         return;
      }
      this.eventId = eventId;
      hasEventId = true;
      setHeaderModified();
   }

   protected void setCorrelationId(EventID correlationId) {
      if (correlationId == null) {
         return;
      }
      this.correlationId = correlationId;
      isCorrelated = true;
      setHeaderModified();
   }

   protected void setTimeToLive(int timeToLive) {
      if (timeToLive == 0) {
         return;
      }
      this.timeToLive = timeToLive;
      hasTimeToLive = true;
      setHeaderModified();
   }

   protected void setPriorityInformation(int priority) {
      if (priority == 0) {
         return;
      }
      this.priority = priority;
      hasPriorityInfo = true;
      setHeaderModified();
   }

   public void setSignatureInformation(String signatureAlgorithm,
                                       byte[] signature) {
      if (signatureAlgorithm == null || signature == null) {
         return;
      }
      this.signatureAlgorithm = signatureAlgorithm;
      this.signature = signature;
      this.hasSignature = true;
      setHeaderModified();
   }

   public void setEncryptedPayloadInformation(byte[] cipherIV,
                                              String cipherMode,
                                              String cipherPaddingScheme) {
      if (cipherMode == null || cipherPaddingScheme == null) {
         return;
      }
      setHeaderModified();
      this.cipherMode = cipherMode;
      this.cipherPaddingScheme = cipherPaddingScheme;
      hasCipherModeAndPadding = true;

      isSecure = true;

      if (cipherIV == null) {
         return;
      }
      this.cipherIV = cipherIV;
      hasCipherIV = true;

   }

   protected void setAssertionsInformation(String assertions) {
      if (assertions != null) {
         if (assertions != "") {
            this.assertions = assertions;
            hasAssertions = true;
            setHeaderModified();
         }
      }
   }

   protected void setIntegrityInformation(String digestScheme,
                                          byte[] messageDigest) {
      this.digestScheme = digestScheme;
      this.messageDigest = messageDigest;
      hasIntegrity = true;
      setHeaderModified();
   }

   protected void setFragmentationInformation(String fragmentationId,
                                              int fragmentNumber,
                                              boolean isLastFragment) {
      if (fragmentationId == null) {
         return;
      }
      this.fragmentationId = fragmentationId;
      this.fragmentNumber = fragmentNumber;
      this.isLastFragment = isLastFragment;
      isFragmented = true;
      setHeaderModified();
   }

   protected void setFragmentationInformation(String fragmentationId,
                                              long originalPayloadLength,
                                              int fragmentNumber,
                                              int totalNumberOfFragments,
                                              long fragmentationEpoch) {
      if (fragmentationId == null) {
         return;
      }

      this.fragmentationId = fragmentationId;
      this.fragmentNumber = fragmentNumber;
      this.totalNumberOfFragments = totalNumberOfFragments;
      this.originalPayloadLength = originalPayloadLength;
      this.fragmentationEpoch = fragmentationEpoch;
      if (fragmentNumber == totalNumberOfFragments) {
         isLastFragment = true;
      }

      hasTotalNumOfFragmentsInfo = true;
      isFragmented = true;
      setHeaderModified();

   }

   protected void setSlicedInformation(int sliceStartingPoint,
                                       int sliceEndingPoint) {
      if (sliceEndingPoint == 0) {
         return;
      }
      this.sliceStartingPoint = sliceStartingPoint;
      this.sliceEndingPoint = sliceEndingPoint;
      isSliced = true;
      setHeaderModified();
   }

   protected void setPayloadCompressionInformation(
                                                   String compressionAlgo,
                                                   long originalPayloadLength,
                                                   boolean isPayloadCompressionFinalStep) {
      if (compressionAlgo == null) {
         return;
      }
      this.compressionAlgo = compressionAlgo;
      this.originalPayloadLength = originalPayloadLength;
      this.isPayloadCompressionFinalStep = isPayloadCompressionFinalStep;
      isCompressed = true;
      setHeaderModified();
   }

   protected void setPersistent() {
      isPersistent = true;
      isTransient = false;
      setHeaderModified();
   }

   // HG: Security related stuff...

   /** Get the Security token from the event */
   public byte[] getSignedSecurityToken() {
      return signedSecurityTokenBytes;
   }

   /** Set the signed security token */
   public void setSecurityToken(byte[] tokenBytes) {
      if (tokenBytes == null)
         isSecure = false;
      else {
         isSecure = true;
         hasSignedSecurityToken = true;
         this.signedSecurityTokenBytes = tokenBytes;
      }
      setHeaderModified();
   }

   public byte[] getSecurityTimeStampedSeqNo() {
      return securityTimeStampedSeqNo;
   }

   public void setSecurityTimeStampedSequenceNo(byte[] obj) {
      if (obj == null)
         return;
      hasSecurityTimeStampedSequenceNo = true;
      securityTimeStampedSeqNo = obj;
      setHeaderModified();
   }

   // --

   /** ********************************************************************* */
   /** ** Additional Utility methods for diagnostics and marshalling */
   /** ********************************************************************* */

   /** Generate the serialized representation of the event headers */
   public byte[] getBytes() {
      ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream(50);
      DataOutputStream dout = new DataOutputStream(baOutputStream);
      
      if (!headerModified && marshalledBytes != null) {
         return marshalledBytes;
      }

      try {
         snapshot = createHeaderSnapshot();
         // System.out.println(moduleName +
         // ByteUtilities.printInt(snapshot));
         /** Marshall the header snapshot first */
         dout.writeInt(snapshot);
         if (snapshot == 0) {
            marshalledBytes = baOutputStream.toByteArray();
            return marshalledBytes;
         }

         if (hasTemplateId) {
            dout.writeInt(templateId);
         }

         if (hasTimestamp) {
            dout.writeLong(timestamp);
         }

         if (hasSource) {
            dout.writeInt(source);
         }

         if (hasEventId) {
            byte[] eventIdBytes = eventId.getBytes();
            dout.writeInt(eventIdBytes.length);
            dout.write(eventIdBytes);
         }

         if (isCorrelated) {
            byte[] corrIdBytes = correlationId.getBytes();
            dout.writeInt(corrIdBytes.length);
            dout.write(corrIdBytes);
         }

         if (hasTimeToLive) {
            dout.writeInt(timeToLive);
         }

         if (isSecure) {
            dout.writeBoolean(hasSignature);
            if (hasSignature) {
               dout.writeInt(signatureAlgorithm.length());
               dout.write(signatureAlgorithm.getBytes());
               dout.writeInt(signature.length);
               dout.write(signature);
            }

            dout.writeBoolean(hasCipherIV);
            if (hasCipherIV) {
               dout.writeInt(cipherIV.length);
               dout.write(cipherIV);
            }

            dout.writeBoolean(hasCipherModeAndPadding);
            if (hasCipherModeAndPadding) {
               dout.writeInt(cipherMode.length());
               dout.write(cipherMode.getBytes());

               dout.writeInt(cipherPaddingScheme.length());
               dout.write(cipherPaddingScheme.getBytes());
            }

            dout.writeBoolean(hasAssertions);
            if (hasAssertions) {
               dout.writeInt(assertions.length());
               dout.write(assertions.getBytes());
            }

            // HG: Marshalling for Signed security token and security
            // timestamp
            dout.writeBoolean(hasSignedSecurityToken);
            if (hasSignedSecurityToken) {
               dout.writeInt(signedSecurityTokenBytes.length);
               dout.write(signedSecurityTokenBytes);
            }

            dout.writeBoolean(hasSecurityTimeStampedSequenceNo);
            if (hasSecurityTimeStampedSequenceNo) {
               dout.writeInt(securityTimeStampedSeqNo.length);
               dout.write(securityTimeStampedSeqNo);
            }

            // ---
         }

         if (hasIntegrity) {
            dout.writeInt(digestScheme.length());
            dout.write(digestScheme.getBytes());
            dout.writeInt(messageDigest.length);
            dout.write(messageDigest);
         }

         if (isFragmented) {
            byte[] fragIdBytes = fragmentationId.getBytes();
            dout.writeInt(fragIdBytes.length);
            dout.write(fragIdBytes);
            dout.writeLong(originalPayloadLength);
            dout.writeInt(fragmentNumber);
            dout.writeLong(fragmentationEpoch);
         }

         if (hasTotalNumOfFragmentsInfo) {
            dout.writeInt(totalNumberOfFragments);
         }

         if (hasPriorityInfo) {
            dout.writeInt(priority);
         }

         if (hasApplicationInfo) {
            dout.writeInt(applicationType.length());
            dout.write(applicationType.getBytes());
         }

         if (isCompressed) {
            dout.writeInt(compressionAlgo.length());
            dout.write(compressionAlgo.getBytes());
            dout.writeLong(originalPayloadLength);
         }

         dout.flush();
         marshalledBytes = baOutputStream.toByteArray();
      } catch (IOException ioe) {
         System.out.println(moduleName + "Error while marshalling EventHeaders"
                            + ioe);
      }
      /** end try-catch block */

      return marshalledBytes;
   }

   private void setHeaderModified() {
      headerModified = true;
   }

   public boolean isModified() {
      return headerModified;
   }
   
   /** Creates a snapshot of the headers contained in the event headers */
   public int createHeaderSnapshot() {
      snapshot = 0;
      numOfHeaders = 0;

      snapshot = updateSnapshot(snapshot, isPayloadCompressionFinalStep);
      snapshot = updateSnapshot(snapshot, isCompressed);
      snapshot = updateSnapshot(snapshot, isPersistent);
      snapshot = updateSnapshot(snapshot, hasApplicationInfo);
      snapshot = updateSnapshot(snapshot, hasPriorityInfo);
      snapshot = updateSnapshot(snapshot, hasTotalNumOfFragmentsInfo);
      snapshot = updateSnapshot(snapshot, isLastFragment);
      snapshot = updateSnapshot(snapshot, isFragmented);
      snapshot = updateSnapshot(snapshot, hasIntegrity);
      snapshot = updateSnapshot(snapshot, isSecure);
      snapshot = updateSnapshot(snapshot, hasTimeToLive);
      snapshot = updateSnapshot(snapshot, isCorrelated);
      snapshot = updateSnapshot(snapshot, hasEventId);
      snapshot = updateSnapshot(snapshot, supressDistributionToSource);
      snapshot = updateSnapshot(snapshot, hasSource);
      snapshot = updateSnapshot(snapshot, hasTimestamp);
      snapshot = updateSnapshot(snapshot, hasTemplateId);

      // System.out.println(ByteUtilities.printInt(snapshot));
      return snapshot;
   }

   /**
    * Update the headers snapshot to indicate if a given header was true or
    * false. A 1 in the bit vector indicates true while a 0 indicates false
    */
   private int updateSnapshot(int snapshot, boolean subHeader) {
      int orWith = 1;
      /**
       * If the widget's value is true, we need to first update the snapshot to
       * contain the subHeader's trace
       */
      if (subHeader) {
         snapshot |= orWith;
      }
      snapshot = snapshot << 1;
      numOfHeaders++;
      if (numOfHeaders == 31) {
         System.out
                   .println(moduleName + "Num of headers = " + numOfHeaders
                            + "Time to create bigger snapshot for the headers ");
      }
      return snapshot;
   }

   /** Retrieves the values of headers based on the snapshot */
   public void getHeadersFromSnapshot() {
      if (snapshot == 0) {
         System.out.println(moduleName
                            + "Snapshot=0, subheaders will have defaults");
      }

      /**
       * The headers have to be retrieved in the reverse order in which they
       * were generated
       */
      hasTemplateId = getHeaderValue();
      hasTimestamp = getHeaderValue();
      hasSource = getHeaderValue();
      supressDistributionToSource = getHeaderValue();
      hasEventId = getHeaderValue();
      isCorrelated = getHeaderValue();
      hasTimeToLive = getHeaderValue();
      isSecure = getHeaderValue();
      hasIntegrity = getHeaderValue();
      isFragmented = getHeaderValue();
      isLastFragment = getHeaderValue();
      hasTotalNumOfFragmentsInfo = getHeaderValue();
      hasPriorityInfo = getHeaderValue();
      hasApplicationInfo = getHeaderValue();
      isPersistent = getHeaderValue();
      isCompressed = getHeaderValue();
      isPayloadCompressionFinalStep = getHeaderValue();
   }

   /**
    * This is used to determine a sub header's boolean value based on the value
    * of the snapshot. Once the bit-wise & have been invoked the bits in the
    * snapshot have to be shifted to the right by 1 bit.
    */
   private boolean getHeaderValue() {
      snapshot = snapshot >> 1;
      int andWith = 1;
      int computedVal = andWith & snapshot;

      if (computedVal != 0) {
         return true;
      }
      return false;
   }

   public void printHeaders() {
      printHeaderInformation(hasTemplateId, "templateId", templateId + "");
      printHeaderInformation(hasTimestamp, "hasTimestamp", timestamp + "");
      printHeaderInformation(hasSource, "hasSource", source + "");
      printHeaderInformation(supressDistributionToSource,
            "supressDistributionToSource", "");
      if (eventId != null)
         printHeaderInformation(hasEventId, "hasEventId", eventId.toString());
      else
         printHeaderInformation(hasEventId, "hasEventId", "NOT SET");

      if (correlationId != null) {
         printHeaderInformation(isCorrelated, "isCorrelated",
               correlationId.toString());
      } else {
         printHeaderInformation(isCorrelated, "isCorrelated", "NOT SET");
      }

      printHeaderInformation(hasTimeToLive, "hasTimeToLive", timeToLive + "");
      printHeaderInformation(isSecure, "isSecure", assertions);
      printHeaderInformation(hasIntegrity, "hasIntegrity", digestScheme);

      if (fragmentationId != null) {
         printHeaderInformation(isFragmented, "isFragmented",
               fragmentationId.toString() + ", fragment number="
                     + fragmentNumber + "");
      } else {
         printHeaderInformation(isFragmented, "isFragmented", "NOT SET");
      }

      printHeaderInformation(isLastFragment, "isLastFragment", "");
      printHeaderInformation(hasTotalNumOfFragmentsInfo,
            "hasTotalNumOfFragmentsInfo", totalNumberOfFragments + "");
      printHeaderInformation(hasPriorityInfo, "hasPriorityInfo", priority + "");
      printHeaderInformation(hasApplicationInfo, "hasApplicationInfo",
            applicationType);
      printHeaderInformation(isPersistent, "isPersistent", "");
      printHeaderInformation(isCompressed, "isCompressed", compressionAlgo);
      printHeaderInformation(isPayloadCompressionFinalStep,
            "isPayloadCompressionFinalStep", "");
   }

   /** Print information regarding individual header */
   private void printHeaderInformation(boolean headerValue, String headerInfo,
                                       String value) {
      String stringRep = headerInfo + " is set to [";
      if (headerValue) {
         stringRep += ("true] value=>" + value);
      } else {
         stringRep += ("false] value=>" + value);
      }
      System.out.println(stringRep);
   }

   /** A test routine to test the functionality */
   public static void main(String[] args) {
      EventHeadersImpl headers1 = new EventHeadersImpl();
      int snapshot;
      headers1.setPersistent();
      headers1.setFragmentationInformation("fileToFragment.big", (long) 2000,
            10, 1000, System.currentTimeMillis());
      headers1.setEventId(new EventID(123, (long) 345, 324));
      headers1.printHeaders();

      byte[] marshalledBytes = headers1.getBytes();
      System.out.println("Size of marshalled stream = "
                         + marshalledBytes.length);

      System.out.print("\n\n\n");
      EventHeadersImpl headers2 = new EventHeadersImpl(marshalledBytes);
      headers2.printHeaders();

   }

}
